/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining a copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using Vj.MathLibrary;
using System.Drawing;
using Vj.ShapeLibrary.ObjConverter;
using Tao.OpenGl;

namespace Vj.ShapeLibrary
{
    public interface IShape
    {
        Vector Position
        { set; get; }
  
        void Draw();
    }

    public abstract class Shape : IShape
    {
        #region Private Members

        //public float[] mat_ambient;
        //public float[] mat_diffuse;
        //public float[] mat_specular;
        //public float[] mat_shininess;

        #endregion

        #region Properties

        protected int id;

        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        protected double mass;

        public double Mass
        {
            get { return mass; }
            set { mass = value; }
        }

        protected Color ambientColor;

        public Color AmbientColor
        {
            get { return ambientColor; }
            set 
            { 
                ambientColor = value;
                SetMaterial();           
            }
        }

        protected Color diffuseColor;

        public Color DiffuseColor
        {
            get { return diffuseColor; }
            set 
            {
                diffuseColor = value;
                SetMaterial();
            }
        }

        protected Color specularColor;

        public Color SpecularColor
        {
            get { return specularColor; }
            set 
            { 
                specularColor = value;
                SetMaterial();
            }
        }

        protected float shininess;

        public float Shininess
        {
            get { return shininess; }
            set 
            {
                shininess = value;
                SetMaterial();
            }        
        }
        
        protected Vector position;

        public Vector Position
        {
            get { return position; }
            set { position = value; }
        }

        protected Vector velocity;

        public Vector Velocity
        {
            get { return velocity; }
            set { velocity = value; }
        }

        protected Vector scale;

        public Vector Scale
        {
            get { return scale; }
            set { scale = value; }
        }

        protected double rotationAngle;

        public double RotationAngle
        {
            get { return rotationAngle; }
            set { rotationAngle = value; }
        }

        protected Vector rotationVector;

        public Vector RotationVector
        {
            get { return rotationVector; }
            set { rotationVector = value; }
        }

        protected Vector normal;

        public Vector Normal
        {
            get { return normal; }
            set { normal = value; }
        }

        protected ObjModel model;

        public ObjModel Model
        {
            get { return model; }
            set { model = value; }
        }

        #endregion

        public Shape()
        {
            position = new Vector(0.0, 0.0, 0.0);
            mass = 1.0;
            normal = new Vector(0.0, 1.0, 0.0);
            scale = new Vector(1.0, 1.0, 1.0);
            rotationAngle = 0.0;
            rotationVector = new Vector(0.0, 0.0, 0.0);
            velocity = new Vector(0.0, 0.0, 0.0);

            ambientColor = Color.Red;
            diffuseColor = Color.Red;
            specularColor = Color.White;
            shininess = 0.0f;
            
            model = new ObjModel();

            SetMaterial();
        }

        protected void SetMaterial()
        {
            float [] mat_ambient = new float[] { (float)ambientColor.R / 255.0f, (float)ambientColor.G / 255.0f, (float)ambientColor.B / 255.0f, 1.0f };
            float [] mat_diffuse = new float[] { (float)diffuseColor.R / 255.0f, (float)diffuseColor.G / 255.0f, (float)diffuseColor.B / 255.0f, 1.0f };
            float [] mat_specular = new float[] { (float)specularColor.R / 255.0f, (float)specularColor.G / 255.0f, (float)specularColor.B / 255.0f, 1.0f };
            float [] mat_shininess = new float[] { shininess };

            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SPECULAR, mat_specular);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, mat_diffuse);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_AMBIENT, mat_ambient);
            Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SHININESS, mat_shininess);        
        }

        public void Draw()
        {
            Gl.glPushMatrix();
            
            Gl.glTranslated(position.X, position.Y, position.Z);
            Gl.glRotated(rotationAngle, rotationVector.X, rotationVector.Y, rotationVector.Z);
            Gl.glScaled(scale.X, scale.Y, scale.Z);

            DrawUnitShape();

            Gl.glPopMatrix();

        }

        private void DrawUnitShape()
        {
            foreach (ObjShape objShape in model.ShapeList)
            {
                if (objShape.VertexArray.Length == 4)
                {
                    Gl.glBegin(Gl.GL_QUADS);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[0].X, (float)objShape.VertexArray[0].Y, (float)objShape.VertexArray[0].Z);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[1].X, (float)objShape.VertexArray[1].Y, (float)objShape.VertexArray[1].Z);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[2].X, (float)objShape.VertexArray[2].Y, (float)objShape.VertexArray[2].Z);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[3].X, (float)objShape.VertexArray[3].Y, (float)objShape.VertexArray[3].Z);

                    Gl.glEnd();
                }
                else if (objShape.VertexArray.Length == 3)
                {
                    Gl.glBegin(Gl.GL_TRIANGLE_STRIP);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[0].X, (float)objShape.VertexArray[0].Y, (float)objShape.VertexArray[0].Z);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[1].X, (float)objShape.VertexArray[1].Y, (float)objShape.VertexArray[1].Z);

                    Gl.glNormal3f((float)objShape.Normal.X, (float)objShape.Normal.Y, (float)objShape.Normal.Z);
                    Gl.glVertex3f((float)objShape.VertexArray[2].X, (float)objShape.VertexArray[2].Y, (float)objShape.VertexArray[2].Z);

                    Gl.glEnd();
                }
                else
                    throw new Exception("Invalid shape.  Must contain only 3 or 4 vertices.");

            }

        }
    }
}